# 安装打包命令
# pyinstaller -D -p C:\\ProgramData\\Miniconda3\\envs\\autopick\\Lib\\site-packages labels.py
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.image import imread
import matplotlib
import cv2
from scipy.ndimage import label, find_objects
from scipy.ndimage import label, find_objects, binary_fill_holes
from skimage.measure import find_contours
from matplotlib.patches import Polygon
import os
import sys

# 顶层组合类
class auto_pick:
    def __init__(self, csvfile_file,threshold=0.5,output_labels_file=r'labels.txt'):
        self.data_pre_processer = data_pre_process(csvfile_file)
        self.data_pre_processer.Set_fence_value()
        self.data_pre_processer.image_enhancement()
        self.labeler = labels(self.data_pre_processer.Ehimage,self.data_pre_processer.image,threshold,csvfile_file)
        
        
    
# 模型推理的类
class model:
    def __init__(self):
        pass


# 提取标签的类
class labels:
    def __init__(self, Ehimage,original_data,threshold,csvfilename,output_labels_file=r'labels.txt'):
        self.Ehimage_data = Ehimage
        self.original_data = original_data
        self.labels = {1:'tumour',2:'artifact'}
        self.output_labels_file = output_labels_file
        self.threshold = threshold
        self.csvfilename = csvfilename
        
        ## labeles ##
        self.average = None
        self.are_count = None
        self.are_sum = None
        self.aspect_ratio = None
        self.area_center = None
        self.rectangle_points = [] 
        self.polygon_points = [] 
        
    def labels_save_rectangle(self):
        # 保存矩形轮廓
        # 根据阈值创建二值图像
        binary_data = np.where(self.Ehimage_data >= self.threshold, 1, 0)
        # 标记连通区域
        labeled_data, num_labels = label(binary_data)
        # 找到每个连通区域的边界
        regions = find_objects(labeled_data)
        # 保存边界
        for region in regions:
            min_x, max_x = region[1].start, region[1].stop
            min_y, max_y = region[0].start, region[0].stop
            self.rectangle_points.append([[min_x, min_x, max_x, max_x, min_x],[min_y, max_y, max_y, min_y, min_y]]) 
        return self.rectangle_points
    
    def labels_save_polygon(self):
        # 保存多边形轮廓
        scaled_data = np.uint8(self.Ehimage_data * 255)
        threshold_value = int(255*self.threshold)
        ret, thresh = cv2.threshold(scaled_data, threshold_value, 255, cv2.THRESH_BINARY)
        contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_L1)
        # cv2.drawContours(scaled_data, contours, -1, (0, 255, 0), 2)
        # cv2.imshow('Thresholded Image', scaled_data)
        # cv2.waitKey(0)
        # cv2.destroyAllWindows()
        # 保存多边形
        # plt.imshow(self.csvfile_data, cmap='jet',vmin=0, vmax=1)
        # plt.show() 
        filtered_contours = []
        for contour in contours:
            area = cv2.contourArea(contour)
            if len(contour) > 2 and area > 0:
                filtered_contours.append(contour)

        contours = filtered_contours
        if contours:
            for contour in contours:
            # 将每个轮廓转换为坐标列表形式
                contour = np.squeeze(contour)
                x = contour[:, 0]
                y = contour[:, 1]
                # 添加起始点，将轮廓闭合
                x = np.append(x, x[0])
                y = np.append(y, y[0])
                self.polygon_points.append([x,y])        
        return self.polygon_points
           
      
    def show_image_with_roi(self,image):
        points = self.polygon_points
        if points != []:
            # 绘制要提取数据的图像
            #data = pd.read_csv(image)
            data = image
            #plt.imshow(data, cmap='jet',vmin=0, vmax=100)    
            # 绘制边界框
            for i,region in enumerate(points):
                plt.plot(region[0], region[1], 'r-', linewidth=1)
                centroid_x = np.mean(region[0])
                centroid_y = np.mean(region[1])
                plt.text(centroid_x, centroid_y, str(i+1), color='r', fontsize=10, ha='center', va='center')
            # 显示图形
            #plt.show(block=False) 
            plt.imshow(data, cmap='jet', vmin=0, vmax=100)
            #plt.ion() 
            plt.show(block=False) 
        else:
            print("未找到可疑点")  
            data = image
            plt.imshow(data, cmap='jet', vmin=0, vmax=100)
            plt.show() 
        
                        
    def Tag_image_main(self): # train程序的主函数入口
        # self.autocheck.image_enhancement()
        # points = self.autocheck.save_polygon()
        points = self.polygon_points
        if points != []:
            for i,region in enumerate(points):
                self.labels_area(region)
                if self.are_count >= 41: # 41是直径大于1mm的像素个数阈值
                    print("请输入区域%d标记分类,接受数字 1:恶性肿瘤 2:良性肿瘤 3:乳头伪影 4:其他伪影..." % (i+1))
                    num1 = int(input("请输入一个整数 :"))
                    self.save_labels(region,num1)  
        else :
            print("未找到可疑点")      
            
    def get_region(self):
        self.labels_save_polygon()
        self.labels_save_rectangle()
        
    def labels_strategy(self,region):#未来加入策略筛选读取配置文件
        self.labels_average_layer()
        self.labels_area_center(region)
        #self.labels_area(region)
        self.labels_sum(region)
        self.labels_WHratio(region)
        self.labels_area_center(region)
        
        
    
    def labels_average_layer(self):
        self.average = np.mean(self.original_data)

    
    def labels_area(self,region):
        data = self.original_data
        row,col = data.shape
        mask = np.zeros((row, col))
        temp_points = np.column_stack((region[0], region[1]))
        #print(temp_points)
        cv2.fillPoly(mask, [temp_points], 1)
        data_with_polygon = np.multiply(data, mask)
        count = np.sum(data_with_polygon != 0)
        self.are_count = count
    
    def labels_area_center(self,region):
        temp_points = np.column_stack((region[0], region[1]))
        area_center = np.mean(temp_points, axis=0)
        rounded_area_center = np.round(area_center, decimals=1)
        self.area_center = rounded_area_center
        
    def labels_sum(self,region):
        data = self.original_data
        row,col = data.shape
        mask = np.zeros((row, col))
        temp_points = np.column_stack((region[0], region[1]))
        cv2.fillPoly(mask, [temp_points], 1)
        masked_data  = np.ma.masked_array(data, mask=1-mask)
        # plt.imshow(mask, cmap='gray')
        # plt.colorbar()
        # plt.show()
        sum_data = np.sum(masked_data)
        self.are_sum = sum_data
    
    def labels_WHratio(self,region):
        temp_points = np.array(list(zip(region[0], region[1])))
        xmin, xmax = np.min(temp_points[:, 0]), np.max(temp_points[:, 0])
        ymin, ymax = np.min(temp_points[:, 1]), np.max(temp_points[:, 1])
        width = xmax - xmin
        height = ymax - ymin
        self.aspect_ratio = width / height
        
    
    def save_labels(self,region,species,label_filename=None):
        if label_filename is None:
            label_filename = self.output_labels_file
        self.labels_strategy(region)
        current_path = os.path.dirname(os.path.abspath(__file__))
        absolute_path = os.path.join(current_path, label_filename)
        print(absolute_path)
        file = open(absolute_path, "a")
        # 文件名 宽 高 单层血红蛋白浓度均值 可疑区域像素个数 可疑区域血红蛋白总值 可疑区域宽高比  区域中心位置 所属种类
        file.write(str(self.csvfilename)+','+str(self.original_data.shape[1])+','+str(self.original_data.shape[0])+','+\
		           str(self.average)+','+str(self.are_count)+','+str(self.are_sum)+','+str(self.aspect_ratio)+','+\
                   str(self.area_center)+','+str(species)+','+str(self.polygon_points)+'\n')
        file.close()
        
      


# 预处理图像的类，未来可能加入后处理
class data_pre_process:
    def __init__(self, csvfile):
        self.csvfile = csvfile
        data = pd.read_csv(csvfile)
        self.image = np.copy(data.values) #原始图像
        self.Ehimage = np.copy(data.values)
    
    def image_enhancement(self): #对图像进行均值归一化，且加入腐蚀操作断开可能的链接点
        # 图像增强的函数实现
        array_copy = np.copy(self.Ehimage)
        mean = np.mean(array_copy)
        array_copy[array_copy < mean] = 0
        max_value = np.max(array_copy)
        array_copy[array_copy > mean] = (array_copy[array_copy > mean] - mean) / (max_value - mean)
        kernel = np.ones((3, 3), np.uint8)
        for i in range(20):
            eroded_data = cv2.erode(array_copy, kernel)
        # plt.imshow(eroded_data, cmap='jet', vmin=0, vmax=1)
        # plt.imshow(self.image, cmap='jet', vmin=0, vmax=100)
        # plt.show()
        #matplotlib.image.imsave(png_name[0]+'3.png', eroded_data, cmap='jet', vmin=0, vmax=1)
        self.Ehimage = eroded_data  

    def Set_fence_value(self):
        data = self.Ehimage 
        mask_inverse = 0.01
        # A cup
        if data.shape[0] == 288 and data.shape[1] == 769:
            x = [28, 133, 219, 290, 324, 397, 534, 635, 693, 750]
            y = [0, 119, 168, 192, 189, 189, 185, 140, 99, 0]
            mask = np.zeros((288, 769), dtype=np.uint8)
            pts = np.column_stack((x, y))
            cv2.fillPoly(mask, [pts], 255)
            data[np.where(mask == 0)] = mask_inverse
        # B cup
        if data.shape[0] == 384 and data.shape[1] == 897:
            x = [55, 120, 260, 437, 647, 741, 787, 792]
            y = [0, 151, 263, 310, 263, 193, 109, 0 ]
            mask = np.zeros((384, 897), dtype=np.uint8)
            pts = np.column_stack((x, y))
            cv2.fillPoly(mask, [pts], 255)
            data[np.where(mask == 0)] = mask_inverse      
        # C cup
        if data.shape[0] == 448 and data.shape[1] == 1089:
            x = [76, 149, 306, 502, 765, 881, 974, 1027]
            y = [0, 149, 308, 368, 325, 245, 142, 0 ]
            mask = np.zeros((448, 1089), dtype=np.uint8)
            pts = np.column_stack((x, y))
            cv2.fillPoly(mask, [pts], 255)
            data[np.where(mask == 0)] = mask_inverse    
        # D cup
        if data.shape[0] == 544 and data.shape[1] == 1089:
            x = [59, 99, 208, 502, 743, 849, 945, 981]
            y = [0, 210, 359, 521, 471, 369, 250, 0 ]
            mask = np.zeros((544, 1089), dtype=np.uint8)
            pts = np.column_stack((x, y))
            cv2.fillPoly(mask, [pts], 255)
            data[np.where(mask == 0)] = mask_inverse 
            #plt.imshow(data, cmap='jet', vmin=0, vmax=100)
        # E cup        
        if data.shape[0] == 608 and data.shape[1] == 1089:
            x = [40, 1049, 1049,40]
            y = [1, 1, 470, 470 ]
            mask = np.zeros((608, 1089), dtype=np.uint8)
            pts = np.column_stack((x, y))
            cv2.fillPoly(mask, [pts], 255)
            data[np.where(mask == 0)] = mask_inverse 
        self.Ehimage = data
        # plt.imshow(self.Ehimage, cmap='jet', vmin=0, vmax=1)
        # plt.show()
    


if __name__=='__main__':
    if len(sys.argv) < 2:
        print("请提供输入文件路径")
        sys.exit()
    ## 获取输入文件路径
    input_file = sys.argv[1]
    auto_pick = auto_pick(input_file,0.6)
    auto_pick.labeler.get_region()
    auto_pick.labeler.show_image_with_roi(auto_pick.labeler.original_data)
    auto_pick.labeler.Tag_image_main()
    
 
